/** 
 * Project Name: farm-core 
 * File Name: TaskBuilder.java 
 * Package Name: com.bugull.farm.core.frame.pool
 * Date: 2017年3月9日上午11:18:02 
 * Copyright (c) 2017, hadlinks All Rights Reserved. 
 * 
 */
package com.bugull.farm.core.frame.pool;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.FutureTask;

/** 
 * ClassName: BuguFutureTask 
 * Function: 自定义的FutureTask，自带任务失败重试功能，在任务移出队列时开放DoneProxy接口给用户进行最终处理.
 * date: 2017年3月9日 上午11:18:02  
 * 
 * @author songwei (songw@hadlinks.com)
 * @version 
 * @since JDK 1.8 
 */
public class BuguFutureTask extends FutureTask<Boolean> {
	
	//失败任务的队列 
	private static Map<String, Integer> failedTasks = new ConcurrentHashMap<String, Integer>();
	private BuguCallable<Boolean> localCallable;
	private DefaultDoneProxyImpl dpi = new DefaultDoneProxyImpl();
	
	public BuguFutureTask(BuguCallable<Boolean> callable) {
		super(callable);
		this.localCallable = callable;
	}
	
	/**
	 * 绑定一个任务代理，在任务从队列中移除时，触发执行DoneProxyImpl中的failed或success方法
	 * @param dpi
	 */
	public void bindDoneProxy(DefaultDoneProxyImpl dpi){
		this.dpi = dpi;
	}

	@Override
	protected void done() {
		try {
			String id = localCallable.getId();
			Integer maxRetryTimes = localCallable.getMaxRetryTime();
			boolean retry = localCallable.isRetry();
			AbstractSmartPool pool = localCallable.getPool();
			boolean success = get();//任务执行标志
			if(!success){//任务执行失败
				if(retry){
					//判断是否已经失败过
					if(failedTasks.containsKey(id)){//已经在失败任务列表中
						int failedTimes = failedTasks.get(id);
						if(failedTimes <= maxRetryTimes){//未达到最大失败次数，继续重试
							failedTasks.put(id, failedTimes + 1);
							pool.submit(localCallable);
						}else{//已达到最大失败次数，移出队列
							failedTasks.remove(id);
							dpi.failed();
						}
					}else{//首次失败的任务，添加到队尾进行重试
						failedTasks.put(id, 1);
						pool.submit(localCallable);
					}
				}else{
					dpi.failed();
				}
			}else{//任务执行成功
				if(retry){
					if(failedTasks.containsKey(id)){//在失败任务列表中，则移除
						failedTasks.remove(id);
					}
					dpi.success();
				}else{
					dpi.success();
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			dpi.failed();
		}
	}
}
